package com.wanho.qz103.hrsaas.process.service.impl;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.wanho.qz103.hrsaas.commons.annontations.Autowired;
import com.wanho.qz103.hrsaas.commons.annontations.Service;
import com.wanho.qz103.hrsaas.commons.annontations.Transactional;
import com.wanho.qz103.hrsaas.commons.exception.DAOException;
import com.wanho.qz103.hrsaas.commons.exception.ServiceException;
import com.wanho.qz103.hrsaas.commons.query.Page;
import com.wanho.qz103.hrsaas.commons.query.Query;
import com.wanho.qz103.hrsaas.commons.util.IDUtils;
import com.wanho.qz103.hrsaas.commons.util.RequestUtils;
import com.wanho.qz103.hrsaas.commons.vo.TableVO;
import com.wanho.qz103.hrsaas.process.constant.ProcessKeyConstant;
import com.wanho.qz103.hrsaas.process.dao.QuitDAO;
import com.wanho.qz103.hrsaas.process.pojos.Quit;
import com.wanho.qz103.hrsaas.process.service.ProcessService;
import com.wanho.qz103.hrsaas.process.vo.DefinitionVO;
import com.wanho.qz103.hrsaas.process.vo.InstanceVO;
import com.wanho.qz103.hrsaas.process.vo.TaskVO;
import com.wanho.qz103.hrsaas.sys.dao.UserDAO;
import com.wanho.qz103.hrsaas.sys.pojos.User;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricDetail;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional
public class ProcessServiceImpl implements ProcessService {
    @Autowired
    private QuitDAO quitDAO ;
    @Autowired
    private UserDAO userDAO ;
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") ;
    private static RepositoryService repositoryService ;
    private static RuntimeService runtimeService ;
    private static TaskService taskService ;
    private static HistoryService historyService ;
    private static ManagementService managementService ;
    static {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        repositoryService = processEngine.getRepositoryService() ;
        runtimeService = processEngine.getRuntimeService() ;
        taskService = processEngine.getTaskService() ;
        historyService = processEngine.getHistoryService() ;
        managementService = processEngine.getManagementService() ;
    }


    /**
     * 分页查询 流程实例
     * @param page 封装的分页查询条件
     * @return
     * @throws ServiceException
     */
    @Override
    public TableVO findPage(Page page) throws ServiceException {
        // 流程引擎
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        // 任务服务
        TaskService taskService = processEngine.getTaskService();
        // 任务查询器
        TaskQuery taskQuery = taskService.createTaskQuery();
        // 任务列表
        long count = taskQuery.count();
        // 当前页任务
        List<Task> taskList = taskQuery.listPage((int) page.getPage(),page.getSize());

        List<InstanceVO> instanceVOList = taskList.stream().map(task -> {
            try {
                InstanceVO instanceVO = new InstanceVO();
                //任务id
                String id = task.getId();
                //流程定义id
                String processDefinitionId = task.getProcessDefinitionId();
                //实例id
                String processInstanceId = task.getProcessInstanceId();
                //获得流程实例对象
                ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                        .processDefinitionId(processDefinitionId)
                        .processInstanceId(processInstanceId)
                        .singleResult();
                //办理人
                String userId = task.getAssignee();
                //流程key
                String processKey = processInstance.getProcessDefinitionKey() ;
                // 申请时间
                Date procApplyTime = task.getCreateTime();
                // 获得 流程发起人
                if (processKey.equals(ProcessKeyConstant.QUIT)) {
                    Quit dbQuit = quitDAO.selectByProInstanceId(processInstanceId);
                    // 根据 id 查询用户对象
                    User sendUser = userDAO.selectById(dbQuit.getUserId());
                    User assigneeUser = userDAO.selectById(userId);
                    instanceVO.setProcessName(dbQuit.getProcessName());
                    instanceVO.setDepartmentId(sendUser.getDepartmentId());
                    instanceVO.setDepartmentName(sendUser.getDepartmentName());
                    instanceVO.setProcData(JSON.toJSONString(dbQuit));
                    instanceVO.setProcCurrNodeUserId(userId);
                    instanceVO.setProcCurrNodeUserName(assigneeUser.getUsername());
                    instanceVO.setUserId(sendUser.getId());
                    instanceVO.setUsername(sendUser.getUsername());
                }else if (processKey.equals(ProcessKeyConstant.OVERTIME)){
                    List<HistoricVariableInstance> historicVariableInstanceList = historyService.createHistoricVariableInstanceQuery()
                            .processInstanceId(processInstanceId)
                            .list();
                    for (HistoricVariableInstance historicVariableInstance : historicVariableInstanceList) {
                        String name = historicVariableInstance.getVariableName();
                        Object value = historicVariableInstance.getValue();
                        if ("mgr".equals(name)){
                            System.out.println("部门经理:" + value);
                        }else if("hr".equals(name)){
                            System.out.println("人事经理:" + value);
                        }else if("bossMgr".equals(name)){
                            System.out.println("总经理:" + value);
                        }else if("form".equals(name)){
                            Map<String,Object> map = (Map<String, Object>) value;
                            String startUserId = (String) map.get("userId");
                            System.out.println("流程发起人的id:" + startUserId);
                        }
                    }
                    System.out.println("加班流程实例变量==============:"+historicVariableInstanceList);
                }
                instanceVO.setProcessId(processInstanceId);
                instanceVO.setProcApplyTime(procApplyTime);
                instanceVO.setProcessDefinitionId(processDefinitionId);
                instanceVO.setProcessState("1");
                return  instanceVO ;
            } catch (DAOException e) {
                e.printStackTrace();
                throw new RuntimeException("查询用户异常！") ;
            }
        }).collect(Collectors.toList());
        TableVO tableVO = new TableVO();
        tableVO.setTotal(count);
        tableVO.setRows(instanceVOList);
        return tableVO;
    }

    @Override
    public List<DefinitionVO> findAllProcessDefinition() throws ServiceException {
        try {
            List<Deployment> deploymentList = repositoryService.createDeploymentQuery()
                    .list();
            List<DefinitionVO> definitionVOList = deploymentList.stream().map(deployment -> {
                DefinitionVO definitionVO = new DefinitionVO();
                definitionVO.setKey(deployment.getKey());
                definitionVO.setName(deployment.getName());
                // 部署 工作流 的描述
                definitionVO.setDescription(deployment.getCategory());
                return definitionVO ;
            }).collect(Collectors.toList());
            return definitionVOList;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("查询流程部署列表异常！") ;
        }
    }

    @Override
    public boolean commit(Map<String, String[]> paramMap) throws ServiceException {
        try {
            String handleOpinion = paramMap.get("handleOpinion")[0] ;
            String processId = paramMap.get("processId")[0] ;
            // 2 同意
            String handleType = paramMap.get("handleType")[0] ;
            String handleUserId = paramMap.get("handleUserId")[0] ;
            Task task = taskService.createTaskQuery()
                    .processInstanceId(processId)
                    .taskAssignee(handleUserId)
                    .singleResult();
            taskService.addComment(task.getId(),processId,handleOpinion);
            if ("2".equals(handleType)){
                taskService.complete(task.getId());
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("申请成功！") ;
        }
    }

    @Override
    public List<TaskVO> findTaskHistoryByProcessInstanceId(String processInstanceId) throws ServiceException {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();
        List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .list();
        List<TaskVO> taskVOList = historicTaskInstanceList.stream().map(historicTaskInstance -> {
            TaskVO taskVO = new TaskVO();
            taskVO.setTaskId(historicTaskInstance.getId());
            taskVO.setHandleOpinion("同意");
            taskVO.setHandleTime(historicTaskInstance.getTime());
            taskVO.setHandleType("1");
            taskVO.setHandleUserId(historicTaskInstance.getAssignee());
            //设置 办理人
            taskVO.setHandleUserName(historicTaskInstance.getAssignee());
            taskVO.setProcessId(historicTaskInstance.getProcessInstanceId());
            taskVO.setTaskKey(historicTaskInstance.getTaskDefinitionKey());
            taskVO.setTaskName(historicTaskInstance.getName());
            return taskVO ;
        }).collect(Collectors.toList());
        return taskVOList;
    }

    /**
     * 查看 流程实例详情
     * @param processInstanceId  流程实例id
     * @return
     * @throws ServiceException
     */
    @Override
    public InstanceVO findProcessInstanceById(String processInstanceId) throws ServiceException {
        try {
            InstanceVO instanceVO = new InstanceVO();
            Task task = taskService.createTaskQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId)
                    .singleResult();
            String processKey = processInstance.getProcessDefinitionKey();
            System.out.println("流程key:"+processKey);
            // 获得 办理人 id
            String assignee = task.getAssignee();
            // 根据 办理人 id 查询办理人信息
            User assigneeUser = userDAO.selectById(assignee) ;
            // 获得 流程发起人
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(processInstance.getId())
                    .singleResult();
            String startUserId = historicProcessInstance.getStartUserId();
            //根据流程发起人的id查询流程发起人信息
            User startUser = userDAO.selectById(assignee) ;
            if (processKey.equals(ProcessKeyConstant.QUIT)) {
                Quit quit = quitDAO.selectByProInstanceId(processInstanceId);
                instanceVO.setProcData(JSON.toJSONString(quit));
                instanceVO.setProcessName(quit.getProcessName());
            }else if (processKey.equals(ProcessKeyConstant.OVERTIME)){
                Map<String, Object> processVariables = historicProcessInstance.getProcessVariables();
                System.out.println("加班流程实例变量:"+processVariables);
            }
            //设置流程key
            instanceVO.setProcessKey(processKey);
            instanceVO.setProcessDefinitionId(processInstance.getProcessDefinitionId());
            instanceVO.setProcessId(processInstance.getProcessInstanceId());
            instanceVO.setProcApplyTime(processInstance.getStartTime());
            instanceVO.setProcCurrNodeUserId(assignee);
            instanceVO.setProcCurrNodeUserName(assigneeUser.getUsername());
            instanceVO.setDepartmentId(startUser.getDepartmentId());
            instanceVO.setDepartmentName(startUser.getDepartmentName());
            instanceVO.setTimeOfEntry(startUser.getTimeOfEntry());
            instanceVO.setUserId(startUserId);
            instanceVO.setUsername(startUser.getUsername());
            instanceVO.setProcEndTime(task.getCreateTime());
            return instanceVO ;
        } catch (DAOException e) {
            e.printStackTrace();
            throw new ServiceException("查看流程实例异常！") ;
        }
    }

    /**
     * 启动： 流程
     * @param paramMap
     * @return
     * @throws ServiceException
     */
    @Override
    public boolean startProcess(Map<String,String[]> paramMap) throws ServiceException {
        try {
            // 获得流程key
            String processKey = paramMap.get("processKey")[0];
            if (processKey.equals(ProcessKeyConstant.QUIT)){
                 this.startQuit(paramMap) ;
            }else if (processKey.equals(ProcessKeyConstant.OVERTIME)){
                this.startOvertime(paramMap);
            }
            return true;
        } catch (DAOException | ParseException e) {
            e.printStackTrace();
            throw new ServiceException("启动流程异常！") ;
        }
    }

    /**
     * 启动 加班流程
     * 表单数据：
     * start_time: 2023-10-11 00:00:00
     * end_time: 2023-10-31 00:00:00
     * reason: 工作没完成，需要加班5天
     * processKey: process_overtime
     * processName: 加班
     * userId: U-CC
     * @param paramMap
     * @return
     */
    private void startOvertime(Map<String,String[]> paramMap) throws ParseException {
        //获得表单数据
        String strStartTime = paramMap.get("start_time")[0];
        String strEndTime = paramMap.get("end_time")[0];
        String reason = paramMap.get("reason")[0];
        String processKey = paramMap.get("processKey")[0];
        String processName = paramMap.get("processName")[0];
        String userId = paramMap.get("userId")[0];
        //类型转换  计算加班天数
        Date start_time = sdf.parse(strStartTime) ;
        Date end_time = sdf.parse(strEndTime) ;
        int days = (int) ((end_time.getTime()-start_time.getTime())/(24*60*60*1000));
        //封装成 动态 pojos
        Map<String,Object> form = new HashMap<>(7) ;
        form.put("start_time",start_time);
        form.put("end_time",end_time);
        form.put("reason",reason) ;
        form.put("processKey",processKey) ;
        form.put("processName",processName) ;
        form.put("userId",userId) ;
        form.put("days",days) ;
        //封装 流程实例变量
        Map<String,Object> dataMap = new HashMap<>() ;
        dataMap.put("form",form) ;
        dataMap.put("mgr","U-GJ") ;
        dataMap.put("hr","U-ZGL") ;
        dataMap.put("bossMgr","U-ZY") ;
        runtimeService.startProcessInstanceByKey(processKey,dataMap) ;
    }

    /**
     * 插入 离职单表数据
     * @param paramMap
     * @return
     * @throws DAOException
     */
    private void startQuit(Map<String,String[]> paramMap) throws DAOException {
        // 获得流程key
        String processKey = paramMap.get("processKey")[0];
        Quit quit = new Quit();
        RequestUtils.parseRequest2Bean(paramMap,quit);
        //获得随机id
        String id = IDUtils.randId();
        quit.setId(id);
        //设置流程变量
        Map<String,Object> map = new HashMap<>() ;
        map.put("quit",quit) ;
        map.put("mgr","U-LB");
        map.put("hr","U-CC") ;
        map.put("devMgr","U-LB");
        map.put("bossMgr","U-CC") ;
        //启动流程
        RuntimeService runtimeService = ProcessEngines.getDefaultProcessEngine().getRuntimeService();
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processKey, map);
        String processInstanceId = processInstance.getProcessInstanceId();
        quit.setProInstanceId(processInstanceId);
        quitDAO.insert(quit) ;
    }
}
